package lightswitch.server.code.object;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JTextField;

import lightswitch.server.code.util.TypeOfData;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class Table {
    private String databaseName;
    private String name;
    private List<Property> listOfProperties;
    private List<Reference> listOfReferences;
    private List<DataRow> listOfDataRows;
    private Vector<JComponent> componentsForInputDatarow;
    
    public static List<DataRow> convertDataRows(List<DataRow> drs, String aliasName){
        List<DataRow> result = new ArrayList<DataRow>();
        for (DataRow dr : drs){
            result.add(new DataRow(DataRow.convertDataRow(dr, aliasName)));
        }
        return result;
    }
    
    public static List<Property> convertProperties(List<Property> ps, String aliasName){
        List<Property> result = new ArrayList<Property>();
        for (Property p : ps){
            result.add(new Property(Property.convertProperty(p, aliasName)));
        }
        return result;
    }
    
    public Table(String name, String databaseName) {
        listOfProperties = new ArrayList<Property>();
        listOfReferences = new ArrayList<Reference>();
        listOfDataRows = new ArrayList<DataRow>();
        componentsForInputDatarow = new Vector<JComponent>();
        setName(name);
        setDatabaseName(databaseName);
    }
    public void setDatabaseName(String databaseName2) {
        databaseName = databaseName2;
    }

    public String getDatabaseName() {
        return databaseName;
    }

    public Table() {
        listOfProperties = new ArrayList<Property>();
        listOfReferences = new ArrayList<Reference>();
        listOfDataRows = new ArrayList<DataRow>();
        componentsForInputDatarow = new Vector<JComponent>();
        setName("null");
        setDatabaseName(null);
    }

    public String getName() {
        return name;
    }

    public Vector<Property> getPrimaryKey() {
        Vector<Property> result = new Vector<Property>();
        for (Property p : listOfProperties) {
            if (p.getIsPrimary()) {
                result.add(p);
            }
        }
        return result;
    }

    public List<Property> getListOfProperties() {
        return listOfProperties;
    }

    public List<Reference> getListOfReferences() {
        return listOfReferences;
    }

    public Property getProperty(int index) {
        return listOfProperties.get(index);
    }
    
    public Property getProperty(String nameField) {
        for (Property p : listOfProperties){
            if (p.getOriginName().equals(nameField))
                return p;
        }
        return null;
    }

    public Reference getReference(int index) {
        return listOfReferences.get(index);
    }

    public DataRow getDataRow(int index) {
        return listOfDataRows.get(index);
    }

    public int getSizeProperties() {
        return listOfProperties.size();
    }

    public int getSizeReferences() {
        return listOfReferences.size();
    }

    public int getSizeDataRows() {
        return listOfDataRows.size();
    }

    public List<DataRow> getListOfDataRows() {
        return listOfDataRows;
    }

    public void setName(String value) {
        name = value.toLowerCase();
    }

    public void setPrimaryKey(Vector<Property> values) {
        for (Property p : listOfProperties) {
            for (Property v : values) {
                if (v.getName().equals(p.getName())) {
                    p.setIsPrimary(true);
                }
            }
        }
    }

    public void setPrimaryKey(String propName) {
        //
        for (Property p : listOfProperties) {
            if (propName.equals(p.getOriginName())) {
                p.setIsPrimary(true);
            }
        }
    }

    public boolean addProperty(Property value) {
        if (listOfProperties.add(value)) {
            componentsForInputDatarow.add(new JLabel(value.getOriginName()));
            componentsForInputDatarow.add(new JTextField());

            if (getSizeProperties()<=1)
                return true;
            
            if (getSizeDataRows() > 0) {
                addNewEntryToAllDataRows(value.getOriginName());
            }

            return true;
        }
        return false;
    }

    private void addNewEntryToAllDataRows(String name2) {
        for (DataRow dr : listOfDataRows) {
            dr.addNullEntry(name2);
        }
    }

    public boolean addProperty(Property value, boolean isPrimaryKey) {
        boolean flag = listOfProperties.add(value);

        if (flag) {
            componentsForInputDatarow.add(new JLabel(value.getOriginName()));
            componentsForInputDatarow.add(new JTextField());
            if (getSizeDataRows() > 0) {
                addNewEntryToAllDataRows(value.getOriginName());
            }
            return true;
        }

        if (isPrimaryKey)
            setPrimaryKey(value.getOriginName());
        return flag;
    }

    public boolean addReference(Reference value) {
        return listOfReferences.add(value);
    }

    public boolean editDataRoẉ̣̣̣̣̣̣̣(Object value, int row, int col){
        return getDataRow(row).editEntry(value, col);
    }
    
    public boolean editDataRoẉ̣̣̣̣̣̣̣(DataRow oldRow, DataRow newRow){
        for (int i=0; i<getSizeDataRows(); i++){
            if (listOfDataRows.get(i).getChainOfValues().equals(oldRow.getChainOfValues())){
                listOfDataRows.set(i, newRow);
                return true;
            }
        }
        
        return false;
    }
    
    
    public boolean isValidValueWithPrimaryKey(DataRow dataRow) {
        List<Property> listPri = getPrimaryKey();
        for (DataRow dr : getListOfDataRows()){
            for (int i=0;i<dr.getSize();i++){
                int count = 0;
                for (int j=0; j<listPri.size(); j++)
                {
                    if (listPri.get(j).getOriginName().equals(dr.getEntry(i).getOriginName()) &&
                    dataRow.getEntry(listPri.get(j).getOriginName()).getValue().equals(dr.getEntry(i).getValue())){
                        count++;
                    }
                }
                if (count >= listPri.size())
                    return false;
            }
        }
        return true;
    }
    
    public boolean addDataRow(DataRow value, boolean isCheck) {
        // check condition
        for (int i = 0; i<value.getSize(); i++){
            String nameOfEntry = value.getEntry(i).getOriginName();
            String valueOfEntry = value.getEntry(i).getValue();
            if (nameOfEntry==null || valueOfEntry==null) return false;
            Property p = getProperty(nameOfEntry);
            if (p == null) return false;
            
            if (!isCheck){
                return listOfDataRows.add(value);
            }
            
            
            // check type
            if (p.getTypeOfData().equals(TypeOfData.INT)){
                try{
                    Integer sampleValue = Integer.parseInt(valueOfEntry);
                } catch(Exception e){
                    return false;
                }
            } else if (p.getTypeOfData().equals(TypeOfData.CHAR)){
            }
            // Check range
            
            if (p.getMaxTypeLength() < (valueOfEntry.length()))
                return false;
            //check primary key
            if (getSizeOfPrimaryKey()>0)
            {
                if (!isValidValueWithPrimaryKey(value)){
                    return false;
                }
            }
            
            // check reference
            if (getSizeReferences()>0)
            {
                if (!isValidValueWithReference(value)){
                    return false;
                }
            }
        }
        
        return listOfDataRows.add(value);
        


        
    }

    private int getSizeOfPrimaryKey() {
        return getPrimaryKey().size();
    }

    public boolean isValidValueWithReference(DataRow value) {
        if (getSizeReferences()==0) return true;
        for (int i=0; i<getSizeReferences(); i++){
            Reference r = getReference(i);
            for (int j=0; j<value.getSize(); j++){
                MyEntry entry = value.getEntry(j);
                if (r.getField().equals(entry.getOriginName())){
                    //get all values of reference type
                    Vector<String> allValues = Database.getAllValueOfFieldInSpecifiedTable(r.getTableRef(), r.getFieldRef());
                    for (String v : allValues){
                        if (entry.getValue().equals(v)){
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }
    public boolean removeReference(String nameRef) {
        for (int i = 0; i < listOfReferences.size(); i++) {
            if (nameRef.equals(listOfReferences.get(i).getName())) {
                listOfReferences.remove(i);
                return true;
            }
        }
        return false;
    }

    public boolean removePrimaryKey(String keyName) {
        for (Property p : listOfProperties){
            if (p.getOriginName().equals(keyName)){
                p.setIsPrimary(false);
                return true;
            }
        }
        return false;
    }

    public boolean removeProperty(String nameProperty) {
        Vector<Reference> refToMe = Database.getRefToMe(this.name);
        if (refToMe.size()!=0){
            for (Reference r : refToMe){
                if (r.getTableRef().equals(this.name) && r.getFieldRef().equals(nameProperty)){
                    return false;
                }
            }
        }
        List<DataRow> rollBack = new Vector<DataRow>();
        if (removeAllDatarowWithProperty(nameProperty, rollBack)) {
            for (int i = 0; i < listOfProperties.size(); i++) {
                if (nameProperty.equals(listOfProperties.get(i).getOriginName())) {
                    if (listOfProperties.remove(i) != null) {
                        componentsForInputDatarow.remove(i * 2);
                        componentsForInputDatarow.remove(i * 2);
                        return true;
                    }
                }
            }
        }
        listOfDataRows = rollBack;
        return false;
    }
    
    public boolean removeProperty(int index) {
        String nameProperty = listOfProperties.get(index).getOriginName();
        Vector<Reference> refToMe = Database.getRefToMe(this.name);
        if (refToMe.size()!=0){
            for (Reference r : refToMe){
                if (r.getTableRef().equals(this.name) && r.getFieldRef().equals(nameProperty)){
                    return false;
                }
            }
        }
        return removeProperty(nameProperty);
    }

    private boolean removeAllDatarowWithProperty(String nameProperty, List<DataRow> temp) {
        temp = listOfDataRows;
        boolean flagFail = false;
        for (DataRow dr : listOfDataRows) {
            if (!dr.removeField(nameProperty)) {
                flagFail = true;
                break;
            }
        }
        if (!flagFail) {
            return true;
        }

        return false;
    }

    public static Element createElement(Document doc, Table table) {
        Element result = doc.createElement("TABLE");
        result.setAttribute("name", table.getName());

        Element properties = doc.createElement("PROPERTIES");
        int nProps = table.getSizeProperties();
        for (int i = 0; i < nProps; i++) {
            Property p = table.getProperty(i);
            properties.appendChild(Property.createElement(doc, p));
        }
        result.appendChild(properties);

        Element references = doc.createElement("REFERENCES");
        int nRefs = table.getSizeReferences();
        for (int i = 0; i < nRefs; i++) {
            Reference r = table.getReference(i);
            references.appendChild(Reference.createElement(doc, r));
        }
        result.appendChild(references);

        Element dataRows = doc.createElement("DATA_ROWS");
        int nDtrs = table.getSizeDataRows();
        for (int i = 0; i < nDtrs; i++) {
            DataRow d = table.getDataRow(i);
            dataRows.appendChild(DataRow.createElement(doc, d));
        }
        result.appendChild(dataRows);

        return result;
    }

    public static Table createTableFromNode(Element tableElement) {
        Table result = new Table();

        Node grandEle = tableElement.getParentNode();
        String databaseName = ((Element) grandEle).getAttribute("name");
        result.setDatabaseName(databaseName);
        String nameTable = tableElement.getAttribute("name");
        result.setName(nameTable);

        NodeList nodeListLV1 = tableElement.getChildNodes();
        for (int i = 0; i < nodeListLV1.getLength(); i++) {
            Node nodeLV1 = nodeListLV1.item(i);

            if (nodeLV1.getNodeType() == Node.ELEMENT_NODE) {
                Element eleLV1 = (Element) nodeLV1;
                String tagName = eleLV1.getTagName();
                if (tagName.equals("PROPERTIES")) {
                    NodeList nodeListLV2 = eleLV1.getChildNodes();
                    for (int j = 0; j < nodeListLV2.getLength(); j++) {
                        Node nodeLV2 = nodeListLV2.item(j);
                        if (nodeLV2.getNodeType() == Node.ELEMENT_NODE) {
                            Element eleLV2 = (Element) nodeLV2;
                            Property p = Property.createPropertyFromNode(eleLV2);
                            result.addProperty(p);
                        }
                    }
                } else if (tagName.equals("REFERENCES")) {
                    NodeList nodeListLV2 = eleLV1.getChildNodes();
                    for (int j = 0; j < nodeListLV2.getLength(); j++) {
                        Node nodeLV2 = nodeListLV2.item(j);
                        if (nodeLV2.getNodeType() == Node.ELEMENT_NODE) {
                            Element eleLV2 = (Element) nodeLV2;
                            Reference r = Reference.createReferenceFromNode(eleLV2);
                            result.addReference(r);
                        }
                    }
                } else if (tagName.equals("DATA_ROWS")) {
                    NodeList nodeListLV2 = eleLV1.getChildNodes();
                    for (int j = 0; j < nodeListLV2.getLength(); j++) {
                        Node nodeLV2 = nodeListLV2.item(j);
                        if (nodeLV2.getNodeType() == Node.ELEMENT_NODE) {
                            Element eleLV2 = (Element) nodeLV2;
                            DataRow dtr = DataRow.createDataRowFromNode(eleLV2);
                            result.addDataRow(dtr, false);
                        }
                    }
                }
            }
        }
        return result;
    }

    public Vector<String> toColumnsViewTable() {
        return Property.toColumnNames();
    }

    public Vector<Vector<Object>> toDataViewTable() {
        Vector<Vector<Object>> result = new Vector<Vector<Object>>();
        for (int i = 0; i < listOfProperties.size(); i++) {
            result.add(Property.toData(listOfProperties.get(i)));
        }
        return result;
    }

    public Vector<String> toColumnsShowTableData() {
        Vector<String> result = new Vector<String>();
        for (int i = 0; i < listOfProperties.size(); i++) {
            result.add(listOfProperties.get(i).getOriginName());
        }
        return result;
    }

    public Vector<Vector<Object>> toDataTableData() {
        Vector<Vector<Object>> result = new Vector<Vector<Object>>();
        for (int i = 0; i < listOfDataRows.size(); i++) {
            result.add(listOfDataRows.get(i).getChainOfValues());
        }
        return result;
    }

    public void setChangeData(boolean isChangeView, int row, int column, Object data, String columnName) {
        if (isChangeView) {
            Property p = listOfProperties.get(row);
            p.setViaName(columnName, data);
            listOfProperties.set(row, p);
        } else {
            DataRow dr = listOfDataRows.get(row);
            dr.setViaName(columnName, data);
            listOfDataRows.set(row, dr);
        }
    }

    public static Vector<String> getVectorNameFromVectorTable(Vector<Table> tables) {
        Vector<String> result = new Vector<String>();
        for (Table tb : tables) {
            result.add(tb.getName());
        }
        return result;
    }

    public JComponent[] getComponentsForGettingInput() {
        if (componentsForInputDatarow.size() <= 0)
            return null;
        JComponent[] result = new JComponent[componentsForInputDatarow.size()];
        componentsForInputDatarow.toArray(result);
        return result;
    }

    
    public boolean addDataRow(JComponent[] inputs) {
        DataRow dr = new DataRow();
        for (int i = 0; i < inputs.length; i += 2) {
            String name = ((JLabel) inputs[i]).getText();
            String value = ((JTextField) inputs[i + 1]).getText();
            String type = getTypeOfProperty(name.toLowerCase());
            dr.addEntry(new MyEntry(name.toLowerCase(), value.toLowerCase(), type.toLowerCase()));
        }
        return addDataRow(dr, true);
    }

    private String getTypeOfProperty(String name) {
        Property p = getProperty(name);
        return p.getTypeOfData();
    }

    public Vector<String> getAllPropertiesName() {
        Vector<String> result = new Vector<String>();
        for (Property p : listOfProperties) {
            result.add(p.getName());
        }
        return result;
    }
    
    public Vector<String> getAllRefsName(){
        Vector<String> result = new Vector<String>();
        for (Reference r : listOfReferences) {
            result.add(r.getName().toLowerCase());
        }
        return result;
    }
    
    public boolean removeDataRow(DataRow dr){
        return listOfDataRows.remove(dr);
    }
    
    
    public boolean removeDataRow(int indexRow){
        
        if (Database.isCanRemove(this.getName(), getDataRow(indexRow))){
            if(listOfDataRows.remove(indexRow)!=null){
                return true;
            }
        }
        return false;
    }
    public Reference getReference(String s) {
        for (Reference r:listOfReferences)
            if (r.getName().equals(s.toLowerCase())){
                return r;
            }
        
        return null;
    }

    public boolean isHaveEntry(MyEntry entryIsRefed) {
        for (DataRow dr : listOfDataRows){
            if (dr.isHaveEntry(entryIsRefed)){
                return true;
            }
        }
        return false;
    }
    
    public String getChainOfPrimaryKeyValue(int index){
        DataRow dr = getDataRow(index);
        return getChainOfPrimaryKeyValue(dr);
    }
    
    public String getChainOfPrimaryKeyValue(DataRow dr){
        if (getSizeDataRows()==0) return null;
        if (getSizeOfPrimaryKey()==0) return null;
        
        String result = "";
        Vector<Property> primarykey = getPrimaryKey();
        for (int i=0; i<dr.getSize(); i++){
            for (Property p : primarykey){
                result += dr.getEntry(p.getOriginName()).getValue()+ ",";
            }
        }
        
        return result;
    }
    
}
